<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>socket 编程基础知识 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="socket 编程基础知识 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2015/03/29/basic-socket-programming/">

	
	<meta property="article:published_time" content="2015-03-29T00:03:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2015/03/29/basic-socket-programming/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2015-03-28T16:00:00.000Z" itemprop="datePublished">
                    2015-03-29
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/程序技术/">程序技术</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">socket 编程基础知识</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>这篇文章介绍了网络的基本概念，socket 编程的基础知识和 C 语言提供的 socket 库使用。</p>
<ol>
<li>本文只考虑 ipv4，不考虑 ipv6。</li>
<li>只考虑网络上 AF_INET socket 类型，不考虑 UNIX 域协议以及其他类型。</li>
</ol>
<p>TL;DR</p>
<h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><h3 id="什么是-socket"><a href="#什么是-socket" class="headerlink" title="什么是 socket"></a>什么是 socket</h3><p>计算机里面最令人烦的就是这些名词，它们都很抽象，而且解释起来就和没有解释差不多。socket 就是这样的一个概念，不过我还是要试着说明一下。</p>
<p>简单来说，<strong>socket 是对底层网络通信的一层抽象，让程序员可以像文件那样操作网络上发送和接收的数据。</strong></p>
<p>再说的详细一点，假设现在你要编程网络程序，进行服务器端和客户端的通信（数据交换）。不适用 socket 的话，你会做下面的一堆事情：</p>
<ul>
<li>管理缓存区来接收和发送数据</li>
<li>告诉操作系统自己要监听某个端口的数据，还有自己处理这些系统调用来读取数据</li>
<li>当没有连接的时候或者另外一端要还没有发送数据时候，要处理 IO 阻塞，把自己挂起</li>
<li>封装和解析 tcp/ip 协议的数据，维护数据发送的顺序</li>
<li>等等</li>
</ul>
<p>做了一大堆东西，发现最重要的还没有做：发送/接收数据。如果有一个程序能够自动帮我们把上面的东西都做掉，这样我们就可以只关心数据的读写，编程就简单的多了。那么这样一个程序就是 socket，它现在已经是操作系统的一部分，在 linux 中是标准的系统调用，只要调用它提供的一组接口（下面会详解常用函数的使用），就能轻松地建立连接，读写数据，关闭连接，让网络操作就像文件操作一样简单。</p>
<p>这下体会到 <code>Everything is file!</code>  unix 哲学的优点了吧。</p>
<p><img src="https://image.slidesharecdn.com/scalelinuxperformance-130224171331-phpapp01/95/linux-performance-analysis-and-tools-16-638.jpg?cb=1362166290" alt=""></p>
<h3 id="通信地址"><a href="#通信地址" class="headerlink" title="通信地址"></a>通信地址</h3><p>现实生活中，两个人要邮寄信件，必须知道对方的地址。网通信也是如此，只不过这里通信的是程序。程序的地址由三元组（ip 地址，端口，协议）界定。</p>
<p>如果你了解网络协议模型的话，你就会知道，ip 地址是网络层用来路由和通信的标识符，端口（port） 是传输层管理的。而 socket 是在这两层之上，所以需要这两个地址来标识。这里的协议指的是 ipv4，ipv6 或者其他协议。</p>
<h3 id="socket-类型"><a href="#socket-类型" class="headerlink" title="socket 类型"></a>socket 类型</h3><p>创建 socket 的时候需要指定 socket 的类型，一般有三种：</p>
<ol>
<li>SOCK_STREAM：面向连接的稳定通信，底层是 TCP 协议，我们会一直使用这个。</li>
<li>SOCK_DGRAM：无连接的通信，底层是 UDP 协议，需要上层的协议来保证可靠性。</li>
<li>SOCK_RAW：更加灵活的数据控制，能让你指定 IP 头部</li>
</ol>
<h3 id="术语表"><a href="#术语表" class="headerlink" title="术语表"></a>术语表</h3><table>
<thead>
<tr>
<th>名称</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>socket</td>
<td>创建一个通信的管道</td>
</tr>
<tr>
<td>bind</td>
<td>把一个地址三元组绑定到 socket 上</td>
</tr>
<tr>
<td>listen</td>
<td>准备接受某个 socket 的数据</td>
</tr>
<tr>
<td>accept</td>
<td>等待连接到达</td>
</tr>
<tr>
<td>connect</td>
<td>主动建立连接</td>
</tr>
<tr>
<td>send</td>
<td>发送数据</td>
</tr>
<tr>
<td>receive</td>
<td>接受数据</td>
</tr>
<tr>
<td>close</td>
<td>关闭连接</td>
</tr>
</tbody>
</table>
<p><img src="https://lh3.ggpht.com/-48jccDGoPY8/UYALpBdKtmI/AAAAAAAAArc/JuqOtnMNOP0/network4_thumb%25255B4%25255D.png?imgmax=800" alt=""></p>
<h3 id="字节序"><a href="#字节序" class="headerlink" title="字节序"></a>字节序</h3><p>不同的计算机对数据的存储格式不一样，比如 32 位的整数 0x12345678，可以在内存里从高到低存储为 12-34-56-78 或者从低到高存储为 78-56-34-12。关于字节序的内容不会详细介绍，不了解的可以自己查阅相关的资料。</p>
<p>但是这对于网络中的数据来说就带来了一个严重的问题，当机器从网络中收到 12-34-56-78 的数据时，它怎么知道这个数据到底是什么意思？</p>
<p>解决的方案也比较简单，在传输数据之前和接受数据之后，必须调用 htonl/htons 或 ntohl/ntohs 先把数据转换成网络字节序或者把网络字节序转换为机器的字节序。</p>
<ul>
<li>TCP 和 UDP 的端口是互不干扰的，也就是说系统可以同时开启 TCP 80 端口和 UDP 80 端口。</li>
<li>socket 不属于任何一层网络协议，它是对 TCP 层的封装，方便网络编程。</li>
</ul>
<h2 id="C-语言-socket-使用"><a href="#C-语言-socket-使用" class="headerlink" title="C 语言 socket 使用"></a>C 语言 socket 使用</h2><h3 id="创建一个-socket"><a href="#创建一个-socket" class="headerlink" title="创建一个 socket"></a>创建一个 socket</h3><pre><code>int socketid = socket(family, type, protocol);
</code></pre><ul>
<li>socketid: socket 描述符，可以看做是一个文件描述符，通过它来读/写数据</li>
<li>family：整数，通信域。<ul>
<li>AF_INET：因特网协议协议，网络地址，最常用。</li>
<li>AF_UNIX，本地通信，文件地址</li>
</ul>
</li>
<li>type：通信类型<ul>
<li>SOCK_STREAM：可靠的，面向连接的服务，TCP 协议</li>
<li>SOCK_DGRAM：不可靠，无连接的服务，UDP 协议</li>
<li>SOCK_RAW：需要自己管理 IP 头部的数据</li>
</ul>
</li>
<li>protocol：协议<ul>
<li>IPPROTO_TCP，IPPROTO_UDP</li>
<li>一般设为 0， 表示使用默认协议</li>
</ul>
</li>
</ul>
<p>如果出错的话，socketid 返回值是 -1。</p>
<h3 id="关闭（close）-socket"><a href="#关闭（close）-socket" class="headerlink" title="关闭（close） socket"></a>关闭（close） socket</h3><pre><code>int status = close(socketid);
</code></pre><p>关闭连接，释放端口。如果有错，返回值 status 为 -1，否则为 0。</p>
<h3 id="绑定（bind）地址三元组到-socket"><a href="#绑定（bind）地址三元组到-socket" class="headerlink" title="绑定（bind）地址三元组到 socket"></a>绑定（bind）地址三元组到 socket</h3><pre><code>int bind(int fd, const struct sockaddr *, socklen_t);
</code></pre><p>把 socket 绑定到某个地址三元组，用于 server 端监听端口。第一个参数是 socket 的描述符，第二个参数 <code>struct sockaddr</code> 是地址结构体，第三个参数是地址结构体的长度。绑定失败的话返回值为负数，否则为 -1，并且设置 <code>errno</code>。</p>
<p>其中最重要的就是地址结构体，它在 <code>netinet/in.h</code> 中被定义：</p>
<pre><code>struct sockaddr_in
{
  short   sin_family; /* must be AF_INET */
  u_short sin_port;   /* 端口号，必须要通过 htons 转换为网络格式 */
  struct  in_addr sin_addr;  /* ip 地址 */
  char    sin_zero[8]; /* Not used, must be zero */
};
</code></pre><p>其中， <code>in_addr</code> 也是在同一个文件夹被定义，格式为：</p>
<pre><code>struct in_addr
{
    uint32_t s_addr; //32位整数
};
</code></pre><p>服务器端的 s_addr 是本机地址，可以用 <code>INADDR_ANY</code> 变量表示接受来自任何地址的连接，记得在使用之前把地址变量初始化为全 0。<br><code>sockaddr</code> 是通用的 socket 地址结构，<code>sockaddr_in</code> 是网络 socket 的结构，参数有一个类型转换的过程。</p>
<h3 id="监听（listen）socket"><a href="#监听（listen）socket" class="headerlink" title="监听（listen）socket"></a>监听（listen）socket</h3><pre><code>listen(sockfd, 5);
</code></pre><p><code>listen</code> 系统调用让进城坚监听在制定的 socket 上面，第一个参数是 socket 描述符，第二个参数是最大连接数，表示发来请求但是没有被 accept 的连接数量。<code>listen</code> 函数在成功时返回 0，失败时返回 -1，并且设置错误代码。</p>
<h3 id="请求连接（connect）"><a href="#请求连接（connect）" class="headerlink" title="请求连接（connect）"></a>请求连接（connect）</h3><p>客户端要连接自己的 socket 和服务器端监听 socket 的方式就是 <code>connect</code>：</p>
<pre><code>int connect(int socket, const struct sockaddr* address, size_t address_len);
</code></pre><p>socket 是客户端本地创建的套接字，<code>address</code> 是服务器的三元组地址。成功调用时，服务器端将收到请求，<code>accept</code> 连接之后，就在两者之间建立了 socket 通信的管道，之后的读写就是直接对 socket 进行操作。</p>
<h3 id="接受（accept）连接"><a href="#接受（accept）连接" class="headerlink" title="接受（accept）连接"></a>接受（accept）连接</h3><pre><code>new_socket = accept(socket_desc, (struct sockaddr *)&amp;client, (socklen_t*)&amp;c);
</code></pre><p>当客户端有连接请求过来时，<code>accept</code> 函数接受该连接，把客户端的 socket 地址信息保存到 <code>client</code> 变量里，新建一个 socket，返回其描述符，然后数据的读写就能通过新 socket 进行。<br>新 socket 的地址和服务器监听 socket 是一样的，如果不关心客户端地址信息的话，可以把第二个和第三个参数都设置为空指针 <code>NULL</code>。</p>
<p>有了 <code>client</code> 变量，就能得到客户端的 ip 和 port ：</p>
<pre><code>char *client_ip = inet_ntoa(client.sin_addr);
int client_port = ntohs(client.sin_port);
</code></pre><p>如果没有客户端连接，<code>accept</code> 函数将会阻塞，直到有连接过来。</p>
<h3 id="读-写（Write）数据"><a href="#读-写（Write）数据" class="headerlink" title="读/写（Write）数据"></a>读/写（Write）数据</h3><p>上面那么多的函数调用，只是建立了服务器端和客户端的连接，算是通信前的准备工作，两者都有了自己的 socket 描述符。<br>有了 socket 描述符，就可以像文件那样进行读写数据：</p>
<pre><code>write(socket_des, message, strlen(message));
read(socket_des, buffer, sizeof(buffer));
</code></pre><p>需要注意的是，<code>read</code> 函数调用是阻塞的，也就是说如果没有数据发送过来的话，该函数会一直等待，直到可以读到数据。</p>
<p><code>read</code>和 <code>write</code> 返回的是实际读写的数据，这个数据最大是 buffer 的大小。如果传输的数据大于 buffer 的话，需要在程序里显式地去读取，否则会出错。</p>
<p>你可能会想，我一直读到返回的数据小于 sizeof(buff) 不就行了。嗯，这是一个解决方案，不过要判断返回值不是 0，因为返回值是 0 表示连接已经中断（需要调用 close 来关闭 socket），而不是没有数据发送过来。</p>
<h3 id="其他常用函数"><a href="#其他常用函数" class="headerlink" title="其他常用函数"></a>其他常用函数</h3><ol>
<li><p>获取 ip 地址</p>
<p> 很多时候，我们只知道服务器的域名，并不知道 ip 地址。<code>gethostbyname</code> 函数就能完成这个功能，<code>netdb.h</code> 文件里有它的定义，它的原型是：</p>
<pre><code>     #include &lt;netdb.h&gt;

     struct hostent * gethostbyname(const char *name);
</code></pre><p> 参数 <code>name</code> 是诸如 <code>www.google.com</code> 的字符串，返回值是 <code>struct hostent</code> 结构体，用来存储得到的地址信息。</p>
<pre><code>     struct hostent
     {
       char *h_name;         /* Official name of host.  */
       char **h_aliases;     /* Alias list.  */
       int h_addrtype;       /* Host address type.  */
       int h_length;         /* Length of address.  */
       char **h_addr_list;       /* List of addresses from name server.  */
     };
</code></pre><p> 如果函数调用失败，返回空指针 <code>NULL</code>。</p>
</li>
</ol>
<ol start="2">
<li><p>把 long 类型的 ip 转换为字符串类型</p>
<pre><code>     #include &lt;arpa/inet.h&gt;

     char *inet_ntoa(struct in_addr);

     int inet_aton(const char *cp, struct in_addr *inp);
</code></pre><p> 上面的函数返回可用的 in_addr 结构体，需要你手动赋值。下面的函数把转换后的结构拷贝到 inp 指向的结构体里面，然后 inp 就可以直接使用了。</p>
</li>
<li><p>把字符串类型的 ip 转换为 long 类型</p>
<pre><code>     #include &lt;arpa/inet.h&gt;

     in_addr_t inet_addr(const char *ip);
</code></pre></li>
<li><p>把字符串转换成整数</p>
<pre><code> int atoi(const char *nptr);
</code></pre><p> 这个可以把从键盘输入的端口号转换成可用的整数。</p>
</li>
<li><p>getpeername：获取连在某个 socket 另一端的客户地址(ip 和 port)</p>
<pre><code>int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
</code></pre><p>返回的信息保存在 addr 结构体里。</p>
</li>
</ol>
<h2 id="简单的-echo-server"><a href="#简单的-echo-server" class="headerlink" title="简单的 echo server"></a>简单的 echo server</h2><p>有了上面的知识，我们就来写一个简单的 echo server。这个 server 的功能非常简单，它默认监听在本机的 54321 端口，接受 client 端连接，然后把客户端发送的数据加上时间戳发送回去。</p>
<pre><code>#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;errno.h&gt;
#include &lt;string.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;time.h&gt;

#define BUFFER_SIZE 1024
#define PORT 54321
#define BACKLOG 5

int setup_sock(int port)
{
    /*
        * This function sets up a socket listening on local port.
        *
        * port: port number to listen on.
        * :return: socket file descriptor.
        */
    int listen_fd;
    struct sockaddr_in serv_addr;

    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&amp;serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(port);

    bind(listen_fd, (struct sockaddr*)&amp;serv_addr, sizeof(serv_addr));
    listen(listen_fd, BACKLOG);

    return listen_fd;
}


void echo_request(int conn_fd)
{
    int n;
    time_t ticks;
    char sendBuff[BUFFER_SIZE];
    char recvBuff[BUFFER_SIZE];

    memset(sendBuff, 0, sizeof(sendBuff));
    memset(recvBuff, 0, sizeof(recvBuff));

    while( (n = recv(conn_fd, recvBuff, sizeof(recvBuff), 0)) &gt; 0)
    {
        ticks = time(NULL);
        snprintf(sendBuff, sizeof(sendBuff), &quot;%.24s: &quot;, ctime(&amp;ticks));

        recvBuff[n] = &#39;\0&#39;;
        printf(&quot;received:  %s&quot;, recvBuff);
        strcat(sendBuff, recvBuff);
        send(conn_fd, sendBuff, strlen(sendBuff), 0);
    }
    printf(&quot;received 0 bytes, close.\n&quot;);
    close(conn_fd);
}


int main(int argc, char *argv[])
{
    int listen_fd = 0, conn_fd = 0;
    socklen_t cli_len;
    struct sockaddr_in cli_addr;

    printf(&quot;start server...\n&quot;);
    memset(&amp;cli_addr, &#39;0&#39;, sizeof(cli_addr));

    listen_fd = setup_sock(PORT);
    printf(&quot;listening on 0.0.0.0 %d...\n&quot;, PORT);

    cli_len = sizeof(cli_addr);
    while(1)
    {
        conn_fd = accept(listen_fd, (struct sockaddr*)&amp;cli_addr, &amp;cli_len);
        printf(&quot;client ip: %s, port: %d\n&quot;,
                inet_ntoa(cli_addr.sin_addr),
                ntohs(cli_addr.sin_port));

        echo_request(conn_fd);
    }
}
</code></pre><p>用 telnet 测试的结果如下：</p>
<p><img src="http://i3.tietuku.com/2bffcd42526f9631.png" alt=""></p>
<p>这个程序没有很好的错误检查，而且每次只能和一个客户端进行通信，后面接进来的客户端必须要等到前面的客户端主动结束之后才能开始。以后会讲到怎么处理多连接的问题，这个例子只是 socket 基础知识的 demo。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="http://www.csd.uoc.gr/~hy556/material/tutorials/cs556-3rd-tutorial.pdf" target="_blank" rel="noopener">Introduction to Sockets Programming in C using TCP/IP</a></li>
<li><a href="http://beej.us/guide/bgnet/" target="_blank" rel="noopener">Beej’s Guide to Network Programming<br>Using Internet Sockets</a></li>
<li><a href="http://www.binarytides.com/socket-programming-c-linux-tutorial/" target="_blank" rel="noopener">socket programming in C on Linux</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="编写自己的 tftp 客户端（1）" href="/2015/04/12/write-tftp-with-python-1/">
        ← 编写自己的 tftp 客户端（1）
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="salt api 配置和使用" href="/2015/03/24/salt-api-usage/">
        salt api 配置和使用 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#概念"><span class="toc-text">概念</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#什么是-socket"><span class="toc-text">什么是 socket</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#通信地址"><span class="toc-text">通信地址</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#socket-类型"><span class="toc-text">socket 类型</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#术语表"><span class="toc-text">术语表</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#字节序"><span class="toc-text">字节序</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#C-语言-socket-使用"><span class="toc-text">C 语言 socket 使用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#创建一个-socket"><span class="toc-text">创建一个 socket</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#关闭（close）-socket"><span class="toc-text">关闭（close） socket</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#绑定（bind）地址三元组到-socket"><span class="toc-text">绑定（bind）地址三元组到 socket</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#监听（listen）socket"><span class="toc-text">监听（listen）socket</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#请求连接（connect）"><span class="toc-text">请求连接（connect）</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#接受（accept）连接"><span class="toc-text">接受（accept）连接</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#读-写（Write）数据"><span class="toc-text">读/写（Write）数据</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#其他常用函数"><span class="toc-text">其他常用函数</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#简单的-echo-server"><span class="toc-text">简单的 echo server</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">socket 编程基础知识</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2015/03/29/basic-socket-programming/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2015/03/29/basic-socket-programming/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
